#include <Memory/Paging.h>
#include <Utility/CLib.h>
#include <Kernel/GlobalData.h>
#include <IntExcept/Debug.h>
#include <IntExcept/Error.h>

/*
@brief 为用户分配栈帧
@param `nPages` 页面数量
@return 栈底地址
@note PTE 第 9 位置为 1，P 位置 0
*/
LPVOID AllocateStackForUser(int nPages)
{
    LPVOID lpRet = NULL; /* 分配的虚拟地址先置为 NULL */
    PageTableEntry* lpPageTableEntry = NULL; /* 页面 PTE 的地址 */
    /* 分页后，页表在逻辑上是连续的 */
    int nBegin = 768 * 1024; /* 页目录第一项不可使用，故从第 1024 项逻辑页表项开始搜索 */
    int nEnd = nBegin;
    BOOL bSuccess = FALSE;
    /* 遍历页表，此过程中若页表缺失，则会触发 #PF，由中断处理程序补充该页表的定义 */
    do /* 遍历用户空间的页表 */
    {
        if (nEnd - nBegin == nPages)
        {
            bSuccess = TRUE;
            break; /* 找到了可连续分配的页面 */
        }
        else
        {
            nBegin--; /* 在每一趟的开始前进一步进行探测 */
            if (0 == (g_SlavePageTable[nBegin / 1024][nBegin % 1024] & (PTE_PRESENT | PTE_ALLOCATED))) /* 页面可分配 */
            {
            }
            else /* 页面不可分配 */
            {
                nEnd = nBegin; /* 同时指向下个待检测位置 */
            }
        }
    } while (nBegin > 1023);
    if (!bSuccess)
    {
        SetLastError(ERROR_NOT_ENOUGH_MEMORY);
        return lpRet;
    }
    /* 找到连续的虚拟页面，预分配 */
    lpRet = (LPVOID)GENERATE_VIRTUAL_ADDRESS(nBegin / 1024, nBegin % 1024, 0); /* 由 PTE 序号得到 PTE 对应的虚拟地址 */
    while (nBegin < nEnd)
    {
        g_SlavePageTable[nBegin / 1024][nBegin % 1024] |= PTE_ALLOCATED | PTE_USER | PTE_WRITEABLE; /* 分配虚拟页面 */
        /* 用户的栈可以发生缺页 */
        nBegin++;
    }
    SetLastError(ERROR_SUCCESS);
    return lpRet;
}